package com.xhj.ipersistent.builder;

import com.alibaba.druid.pool.DruidDataSource;
import com.xhj.ipersistent.config.Configuration;
import com.xhj.ipersistent.io.Resources;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.util.List;
import java.util.Properties;

/**
 * XMLConfigurationBuilder
 *
 * @author XJks
 * @description 通过XML配置文件构建Configuration的构建器
 *              TODO:
 *                  这里的XML配置文件指的是ipersistent-config.xml文件，而不是XXXMapper.xml文件。
 *                  ipersistent-config.xml文件主要配置数据源等信息，以及所管理的的XXXMapper.xml文件路径。
 *                  XXXMapper.xml文件是对应实体类数据表的映射文件，里面配置了SQL语句等信息。
 */
public class XMLConfigurationBuilder {
    /**
     * 全局配置对象
     */
    private Configuration configuration;

    /**
     * TODO:
     *  构造方法，初始化一个Configuration对象。
     *  在parse方法中，通过解析传入的ipersistent-config.xml配置文件，填充Configuration对象。
     */
    public XMLConfigurationBuilder() {
        this.configuration = new Configuration();
    }

    /**
     * TODO:
     *  解析XML配置文件，返回配置对象Configuration。
     *  该方法主要使用dom4j解析XML文件，并将解析结果填充到Configuration对象中
     *
     * @param resourceAsStream ipersistent-config.xml配置文件对应的输入流
     * @return
     */
    public Configuration parse(InputStream resourceAsStream) throws DocumentException {
        // 使用dom4j中SAXReader()解析XML配置文件，获取Document对象
        Document document = new SAXReader().read(resourceAsStream);
        // 获取XML文件的根节点，即<configuration></configuration>标签
        Element rootElement = document.getRootElement();
        // 获取根节点下的<dataSource></dataSource>标签
        Element dataSourceElement = rootElement.element("dataSource");
        // 获取dataSourceElement标签下的所有<property></property>标签
        List<Element> propertyElementListOfDataSource = dataSourceElement.selectNodes("//property");
        /**
         * TODO:
         *  解析property标签，封装到Properties对象中。
         *  dataSource标签下的property标签主要配置数据源信息，如驱动、URL、用户名、密码等。示例如下:
         *  <pre>
         *      {@code
         *          <dataSource>
         *              <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
         *              <property name="url" value="jdbc:mysql:///mybatis_study?useSSL=false&amp;characterEncoding=UTF-8&amp;serverTimezone=UTC"></property>
         *              <property name="username" value="root"></property>
         *              <property name="password" value="123.com"></property>
         *          </dataSource>
         *      }
         *  </pre>
         */
        Properties properties = new Properties();
        for (Element element : propertyElementListOfDataSource) {
            String name = element.attributeValue("name");
            String value = element.attributeValue("value");
            properties.setProperty(name, value);
        }
        // 创建数据源对象，并设置相关属性。TODO:这里使用的是DruidDataSource，大家也可以选择其他实现类。
        DruidDataSource druidDataSource = new DruidDataSource();
        druidDataSource.setDriverClassName(properties.getProperty("driverClassName"));
        druidDataSource.setUrl(properties.getProperty("url"));
        druidDataSource.setUsername(properties.getProperty("username"));
        druidDataSource.setPassword(properties.getProperty("password"));
        // 创建好的数据源对象封装到Configuration对象中
        configuration.setDataSource(druidDataSource);
        /**
         * TODO:
         *  解析mappers标签，获取所有对应实体类数据表的映射配置文件XXXMapper.xml，并进行加载解析。
         *  1.获取实体类数据表的映射配置文件XXXMapper.xml的文件路径
         *  2.使用Resources工具类，读取XXXMapper.xml文件为输入流
         *  3.使用XMLMappedStatementBuilder解析XXXMapper.xml文件，并将解析结果封装到Configuration对象中
         */
        // 获取根节点下的<mappers></mappers>标签
        Element mappersElement = rootElement.element("mappers");
        // 获取mappers标签下的所有<mapper></mapper>标签
        List<Element> mapperElementListOfMappers = mappersElement.selectNodes("//mapper");
        for (Element element : mapperElementListOfMappers) {
            // 获取XXXMapper.xml文件路径
            String resourcePath = element.attributeValue("resource");
            InputStream resourceAsSteam = Resources.getResourceAsStream(resourcePath);
            // 使用XMLMappedStatementBuilder解析XXXMapper.xml文件
            XMLMappedStatementBuilder xmlMappedStatementBuilder = new XMLMappedStatementBuilder(configuration);
            xmlMappedStatementBuilder.parse(resourceAsSteam);
        }
        return configuration;
    }
}
